<template>
<div class="ebook-reader">
  <!-- 接受动态路由参数的方法{{$router.params.fileName}} -->
     <!--用来展示电子书 -->
  <div id="read"></div>
  <!-- 获取蒙版的点击事件offsetY：Y轴点击的偏移量,在index中监听Y的变化 -->
  <div class="ebook-reader-mask"
       @click="onMaskClick"
       @touchmove="move"
       @touchend="moveEnd"
       @mousedown.left="onMouseEnter"
       @mousemove.left="onMouseMove"
       @mouseup.left="onMouseEnd">
  </div>
</div>
</template>

<script>
  import { ebookMixin } from '../../utils/mixin'
  import Epub from 'epubjs'
  import {
    getFontFamily,
    saveFontFamily,
    getFontSize,
    saveFontSize,
    getTheme,
    saveTheme,
    getLocation
     } from '../../utils/localStorage'
  import { flatten } from '../../utils/book'
  import { getLocalForage } from '../../utils/localForage'
  // 引入样式文件
    //  import { addCss } from '../../utils/book

  global.epub = Epub
  export default {
    mixins: [ebookMixin],
    methods: {
      // 1-鼠标进入
      // 2-鼠标进入后移动
      // 3-鼠标从移动状态松手
      // 4- 鼠标还原
      onMouseEnter(e) {
        // console.log('enter', e)
        this.mouseState = 1
        this.mouseStartTime = e.timeStamp
        e.preventDefault()
        e.stopPropagation()
      },
      onMouseMove(e) {
        // console.log('move', e)
        if (this.mouseState === 1) {
          this.mouseState = 2
        } else if (this.mouseState === 2) {
          let offsetY = 0
          if (this.firstOffsetY) {
            offsetY = e.clientY - this.firstOffsetY
            this.setOffsetY(offsetY)
          } else {
            // changedTouches[0]第一个点击触控的点
            this.firstOffsetY = e.clientY
        }
        }
        e.preventDefault()
        e.stopPropagation()
      },
      onMouseEnd(e) {
        // console.log('end', e)
        if (this.mouseState === 2) {
          this.setOffsetY(0)
          this.firstOffsetY = null
          this.mouseState = 3
        } else {
          this.mouseState = 4
        }
        const time = e.timeStamp - this.mouseStartTime
        if (time < 100) {
          this.mouseState = 4
        }
        e.preventDefault()
        e.stopPropagation()
      },
      // 移动事件
      move (e) {
        // console.log('move', e)
        let offsetY = 0
        if (this.firstOffsetY) {
          offsetY = e.changedTouches[0].clientY - this.firstOffsetY
          this.setOffsetY(offsetY)
        } else {
          // changedTouches[0]第一个点击触控的点
          this.firstOffsetY = e.changedTouches[0].clientY
        }
        e.preventDefault()
        e.stopPropagation()
      },
      // 移动事件结束
      moveEnd (e) {
        // console.log('end', e)
        // 松手后跳回去
        this.setOffsetY(0)
        this.firstOffsetY = null
      },
      // 蒙版点击事件,e蒙版的点击事件对象，上一页，下一页，切换菜单栏
      onMaskClick(e) {
        if (this.mouseState && (this.mouseState === 2 || this.mouseState === 3)) {
          return
        }
        // console.log(e)
        // e.offsetX X轴点击的偏移量
        const offsetX = e.offsetX
        const width = window.innerWidth
        if (offsetX > 0 && offsetX < width * 0.3) {
          this.prevPage()
        } else if (offsetX > 0 && offsetX > width * 0.7) {
          this.nextPage()
        } else {
          this.toggleTitleAndMenu()
        }
      },
      // 返回上一页
      prevPage () {
        if (this.rendition) {
          this.rendition.prev().then(() => {
            this.refreshLocation()
          })
          // 在mixin中
          this.hideTitleAndMenu()
        }
      },
      // 到下一页
      nextPage () {
        if (this.rendition) {
          // console.log('this.rendition', this.rendition)
          this.rendition.next().then(() => {
            this.refreshLocation()
          })
          this.hideTitleAndMenu()
        }
      },
      // 标题底部呼出与隐藏方法封装
      toggleTitleAndMenu () {
        // console.log('menu and title...')
        // 调用store去分发一个事件，调用setMenuVisible方法，调用时候获取他的menuVisible同时取他的反
        // this.$store.dispatch('setMenuVisible', !this.menuVisible)
        this.setMenuVisible(!this.menuVisible)
        // console.log(this.menuVisible)
        if (this.menuVisible) {
          this.setSettingVisible(-1)
        }
        this.setFontFamilyVisible(false)
      },
      // 字号设置封装
      initFontSize () {
        const fontSize = getFontSize(this.fileName)
          if (!fontSize) {
            saveFontSize(this.fileName, this.defaultFontSize)
          } else {
            this.rendition.themes.fontSize(fontSize)
            this.setDefaultFontSize(fontSize)
          }
      },
      // 设置字体封装
      initFontFamily () {
        const font = getFontFamily(this.fileName)
          if (!font) {
            saveFontFamily(this.fileName, this.defaultFontFamily)
          } else {
            this.rendition.themes.font(font)
            this.setDefaultFontFamily(font)
          }
      },
      initTheme () {
        let defaultTheme = getTheme(this.fileName)
        if (!defaultTheme) {
          // 设置默认主题
          defaultTheme = this.themeList[0].name
          saveTheme(this.fileName, defaultTheme)
        }
        this.setDefaultTheme(defaultTheme)
        // 注册主题
        this.themeList.forEach(theme => {
          this.rendition.themes.register(theme.name, theme.style)
        })
        // 电子书选择默认样式
        this.rendition.themes.select(defaultTheme)
      },
      // 表示渲染的过程，rendition渲染对象，使用display()进行展示。'read'表示dom绑定
      initRendition () {
        this.rendition = this.book.renderTo('read', {
          width: innerWidth,
          height: innerHeight,
          method: 'default'// 微信的兼容性
        })
        // console.log(this.rendition)
        // this.rendition.display().then(() => {
        //   this.initTheme()
        //   this.initFontSize()
        //   this.initFontFamily()
        //   this.initGlobalStyle()
        //   this.refreshLocation()`
        // })
        const location = getLocation(this.fileName)
        // console.log(location)
        // 在我们将电子书display后,我们需要初始化
        this.display(location, () => {
        this.initTheme()
        this.initFontSize()
        this.initFontFamily()
        // 初始化全局样式,在mixin中
        this.initGlobalStyle()
        })
        // 解决字体引入问题，将样式文件添加进阅读器dom中，使用rendition的钩子函数，content表示阅读器渲染完后，可以获取到资源文件时，来调用Register方法
        // ，传入一个contents对象，contents对象主要用来管理资源，contens对象下面为我们提供了一个方法addStylessheet（），表示可以手动的
        // 添加样式文件，里面时link的rul路径，使用nginx生成url路径，这个方法返回时promise对象。
        this.rendition.hooks.content.register(contents => {
          Promise.all([
            // 对环境变量进行引用,注意时小撇号不是单引号，环境变量只能一次性载入，完成后要重启项目
            contents.addStylesheet(`${process.env.VUE_APP_RES_URL}/fonts/daysOne.css`),
            contents.addStylesheet(`${process.env.VUE_APP_RES_URL}/fonts/cabin.css`),
            contents.addStylesheet(`${process.env.VUE_APP_RES_URL}/fonts/montserrat.css`),
            contents.addStylesheet(`${process.env.VUE_APP_RES_URL}/fonts/tangerine.css`)
            ]).then(() => {})
        })
      },
      // 初始化全局样式
      // initGlobalStyle() {
      //   addCss('将ngnix的的link标签添加过来')
      // },
      // 翻页等手势功能封装
      initGesture() {
        // 可以用rendition渲染对象的on方法将事件绑定到iframe里面，绑定touchstart触摸开始事件
        this.rendition.on('touchstart', event => {
          // 获取触碰的坐标和时间
          // console.log(event)
          // 再这个event事件下面，clietX表示当前点击的位置，timeStamp手势操作的时间
          this.touchStartX = event.changedTouches[0].clientX
          this.touchStartTime = event.timeStamp
        })
        // 绑定touchend触摸结束事件
        this.rendition.on('touchend', event => {
          // 离开时，判断offsetX：X轴的偏移量，离开建进入的，
          const offsetX = event.changedTouches[0].clientX - this.touchStartX
          const time = event.timeStamp - this.touchStartTime
          // console.log(offsetX, time)
          // 左到右>0
          if (time < 500 && offsetX > 40) {
            this.prevPage()
            // console.log('上一页')
          } else if (time < 500 && offsetX < -40) {
            this.nextPage()
            // console.log('下一页')
          } else {
            // 标题底部呼出与隐藏方法
            this.toggleTitleAndMenu()
          }
          // 禁用这个事件的默认方法调用,禁止他进行传播
          event.preventDefault()
          event.stopPropagation()
        })
      },
      // 获取封面的图片链接以及图书的基本信息,目录
      parseBook() {
        // 获取封面loaded表示电子书正在加载的状态
        // console.log('parseBook')
        this.book.loaded.cover.then(cover => {
          // console.log(cover)
          // cover对象不是一个连接
          this.book.archive.createUrl(cover).then(url => {
            // 将封面连接url存储在vuex中
            this.setCover(url)
            // console.log(url)
          })
        })
        // 获取标题和作者信息
        // console.log(this.book && this.book.loaded)
        // matedate封面的基础信息
        if (this.book && this.book.loaded) {
          this.book.loaded.metadata.then(metadata => {
          // console.log('metadata')
          this.setMetadata(metadata)
        })
        }
        // 获取目录,toc一级目录数组,filter()上一级数组,拍扁
        // 获取目录信息，如何将目录树状结构转化为一个一维数组
        this.book.loaded.navigation.then(nav => {
          console.log('目录对象nav', nav)
          // console.log(flatten(nav.toc))
          // nav的href目录的路径通过这个路径传入dispaly方法（已经定义好的）就可以对电子书进行渲染，lable目录的内容，
          // flatten()方法将目录树拍扁
          const navItem = flatten(nav.toc)
          // 每一个元素传入进去都会获取对应的层级，并加上level字段
          function find(item, level = 0) {
            return !item.parent ? level : find(navItem.filter(parentItem =>
            parentItem.id === item.parent)[0], ++level)
            }
          navItem.forEach(item => {
            item.level = find(item)
          })
          // console.log(navItem)
          this.setNavigation(navItem)
        })
      },
      // epubjs实例化，获取book对象
      initEpub (url) {
        // console.log('initEpub')
        // const url = process.env.VUE_APP_RES_URL + '/epub/' + this.fileName + '.epub'
        // 电子书的解析和渲染
        // epubjs实例化，获取book对象，这样电子书就解析成功了
        this.book = new Epub(url)
        // console.log(this.book)
        // 解析成功后渲染，使用book对象的renderTo方法来实现
        // this.book.renderTo('read', {
          // 指定宽高
        //   width: innerWidth,
        //   height: innerHeight,
        //   method: 'default'// 微信的兼容性
        // })
        this.setCurrentBook(this.book)
        // 渲染初始化过程封装,初始化字体字号,主题,进度条,目录
        this.initRendition()
        // this.initGesture()
        // 解析电子书的内容
        this.parseBook()
        // 电子书的分页方法
        // ready这个钩子函数会在book下面解析完成后调用，ready调用异步方法来做分页
        this.book.ready.then(() => {
        // generate()里的参数，参数传入每页要显示的文字数
        // 生成locations对象
        return this.book.locations.generate(750 * (window.innerWidth / 375) * (getFontSize(this.fileName / 16)))
        .then(locations => {
          // locations包含所有页数信息的数组，分页完成后
          this.setBookAvailable(true)
          // 分页完成后更新进度,章节
          this.refreshLocation()
          // 告诉一共分出多少页,每一页都有一个epub.cfi进行指定.epub.cfi:epub的核心标准用于电子书的定位,可以定位到电子书的任意位置
          // console.log(locations)
          this.navigation.forEach(nav => {
            nav.pagelist = []
          })
          // 将locations对应到各个木木章节中去
          locations.forEach(item => {
            // console.log(locations, this.navigation)
            // locations每个epubcfi前面的信息与navigation的href是相同的
            // 可以通过相等来判断那些页数是处于这个章节的
            // loc获取的locations每个epubcfi前面的信息，就是方括号里的信息
            const loc = item.match(/\[(.*)\]!/)[1]
            // console.log(loc)遍历目录看到底属于那个目录
            this.navigation.forEach(nav => {
              if (nav.href) {
                // ***.html
                // console.log('阅读界面', nav.href)将href后面的html舍去掉
                const href = nav.href.match(/^(.*)\.html$/)[1]
                if (href === loc) {
                  // 如果相等就nav下创建属性pagelist，
                  nav.pagelist.push(item)
                }
              }
            })
            // 初始页数
            let currentPage = 1
            // 设置每个目录的章节的初始页数，根据里面的pagelist，吃屎每个目录下都有一个page，目录右边的页数就是这个显示的
            this.navigation.forEach((nav, index) => {
              if (index === 0) {
                currentPage = 1
                nav.page = 1
              } else {
                nav.page = currentPage
              }
              currentPage += nav.pagelist.length + 1
            })
            // console.log(this.navigation)
          })
          // console.log(this.navigation)
          // console.log(locations)
          // 此时设置进度条可以拖动
          // this.locations()
          // 改造后的locations列表
          this.setPagelist(locations)
          // 可显示进度条了
          // this.setBookAvailable(true)
          // this.refreshLocation()
        })
        })
      }
    },
    mounted () {
      // console.log('mounted() ----- ebookreader', '')
      // 正则表达式，将路由拼成ngnix路径，split('|')将|前后进行划分，后面再用\拼在一起
      // this.$route.params.fileName接受的动态路由
      const books = this.$route.params.fileName.split('|')
      // 获取参数中的fileName
      const fileName = books[1]
      getLocalForage(fileName, (err, blob) => {
        if (!err && blob) {
          console.log('找到离线缓存电子书')
          this.setFileName(books.join('/')).then(() => {
            this.initEpub(blob)
          })
        } else {
          // console.log('在线获取电子书')
          this.setFileName(books.join('/')).then(() => {
          const url = process.env.VUE_APP_RES_URL + '/epub/' + this.fileName + '.epub'
          this.initEpub(url)
      })
        }
      })
    }
  }
</script>

<style lang="scss" rel="stylesheet/scss" scoped>
  @import "../../assets/styles/global";
  .ebook-reader {
    width: 100%;
    height: 100%;
    overflow: hidden;
    .ebook-reader-mask {
      position: absolute;
      top: 0;
      left: 0;
      background: transparent;
      z-index: 150;
      width: 100%;
      height: 100%;
    }
  }
</style>
